#ifndef CLUSNET_BASE_DYNAMICBUFFER_H
#define CLUSNET_BASE_DYNAMICBUFFER_H

#include <string>
#include <cstring>
#include <cassert>
#include <algorithm>

namespace clusnet
{
namespace base
{

class DynamicBuffer
{
        static const size_t kInitialSize = 128;
        static const int kMaxNumericSize = 32;
        static const char digits[20]; 
        static const size_t zero = 9;
        static const char digitsHex[17];
 
    public:
        DynamicBuffer(const DynamicBuffer& that);
        DynamicBuffer(DynamicBuffer&& that);
        DynamicBuffer& operator= (const DynamicBuffer& that);
        DynamicBuffer& operator= (DynamicBuffer&& that);
        explicit DynamicBuffer(size_t initialSize = kInitialSize);
        virtual ~DynamicBuffer();

    public:
        void append(const char* buf, size_t len);

        template<typename T, typename = typename std::enable_if<std::is_integral<T>::value>::type>
        void appendInteger(T val)
        {
            if (avail() < kMaxNumericSize)
            {
                size_t newSize = (kMaxNumericSize / len_ + 2) * len_;
                resize(newSize);
            }
            assert(avail() >= kMaxNumericSize);
            T i = val;
            char* start = data_ + cur_;

            do
            {
                size_t lsd = static_cast<int>(i % 10) + zero;
                i /= 10;
                put(digits[lsd]);
            } while (i != 0);

            if (val < 0)
            {
                put('-');
            }
            std::reverse(start, data_ + cur_);
        }

        void appendHex(uintptr_t val)
        {
            if (avail() < kMaxNumericSize)
            {
                size_t newSize = (kMaxNumericSize / len_ + 2) * len_;
                resize(newSize);
            }
            assert(avail() >= kMaxNumericSize);
            put('0');
            put('x');
            char* start = data_ + cur_;

            do
            {
                size_t lsd = static_cast<size_t>(val % 16);
                val /= 16;
                put(digitsHex[lsd]);
            } while (val != 0);
            std::reverse(start, data_ + cur_);
        }

        void appendDouble(double v)
        {
            if (avail() < kMaxNumericSize)
            {
                size_t newSize = (kMaxNumericSize / len_ + 2) * len_;
                resize(newSize);
            }
            int len = snprintf(data_ + cur_, kMaxNumericSize, "%.12g", v);
            cur_ += len;
            assert(static_cast<size_t>(len) < kMaxNumericSize);
        }

        // 执行append操作后,data_会失效
        size_t length() const { return cur_; }
        char* begin() { return data_; }
        const char* cbegin() const { return data_; } 

        void reset() { cur_ = 0; }
        void bzero() { std::memset(data_, 0, len_); }
        void resize(size_t newSize);

        std::string toString() const { return std::string(data_, cur_); }

    private:
        void put(char c)
        {
            assert(avail() >= 1);
            *(data_ + cur_) = c;
            cur_++;
        }

        void reset(const char* buf, size_t cur, size_t len);
        void destroy();
        size_t  avail() const { return len_ - cur_; }
    
    private:
        char*   data_;
        size_t  len_;
        size_t  cur_;
};
}
}
#endif 
